package com.gitee.l0km.casban;

import com.gitee.l0km.aocache.annotations.AoWeakCacheable;
import com.gitee.l0km.casban.utils.CollectionUtils;
import com.gitee.l0km.com4j.basex.bean.BeanPropertySupport;
import com.gitee.l0km.common.spring.core.annotation.AnnotatedElementUtils;
import com.gitee.l0km.common.spring.core.annotation.TypeMappedAnnotations;
import com.google.common.base.Function;
import com.google.common.base.Functions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 提供常用的 {@link Predicate} 实现工具类，用于各种过滤场景。
 * <p>
 * 主要功能：
 * <ul>
 *   <li>提供基于包名的过滤谓词</li>
 *   <li>提供基于类特征的过滤谓词（如公共类、抽象类等）</li>
 *   <li>提供基于注解的过滤谓词</li>
 *   <li>提供基于类型层次的过滤谓词</li>
 *   <li>提供常用字符串匹配谓词（如前缀、后缀匹配）</li>
 * </ul>
 *
 * <p>
 * 使用示例：
 * <pre>
 * {@code
 *   // 过滤指定包路径下的类
 *   Predicate<String> packageFilter = CommonPredicates.basePackageFilter("com.example");
 *
 *   // 过滤公共类
 *   Predicate<Class<?>> publicClassFilter = CommonPredicates.publicClassFilter();
 *
 *   // 过滤带有特定注解的类
 *   Predicate<Class<?>> annotatedFilter = CommonPredicates.hasAnnotationClassFilter(MyAnnotation.class);
 * }
 * </pre>
 * </p>
 *
 * <p>
 * 注意事项：
 * <ul>
 *   <li>线程安全：本类提供的所有方法都是线程安全的</li>
 *   <li>性能：大部分谓词方法都使用了弱引用缓存以提高性能</li>
 *   <li>内存：使用弱引用缓存，不会导致内存泄漏</li>
 * </ul>
 *
 * @author guyadong
 */
public class CommonPredicates {
	public static Predicate<Class<?>> subtypeFilter(Class<?>clazz){
		return Predicates.subtypeOf(clazz);
	}
	public static final Predicate<Class<?>> enumFilter() {
		return Predicates.subtypeOf(Enum.class);
	}
	
	@SuppressWarnings("unchecked")
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static <T> Predicate<T> noEmptyFilter(){
		return (Predicate<T>) new Predicate<Object>(){
			@Override
			public boolean apply(Object input) {
				return !BeanPropertySupport.isEmpty(input);
			}};
	}
	
	public static Predicate<String> startsWithFilter(String prefix){
		return startsWithFilter(Collections.singleton(prefix));
	}

	public static Predicate<String> startsWithFilter(String... prefixes){
		checkNotNull(prefixes,"prefixes is null");
		return startsWithFilter(FluentIterable.from(prefixes));
	}
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<String> startsWithFilter(Iterable<String> prefixes){
		checkNotNull(prefixes,"prefixes is null");
		Set<String> _prefixes = CollectionUtils.toNonullLinkedHashSet(prefixes, null);
		return new StartsWithPredicate(_prefixes,false);
	}

	public static Predicate<String> basePackageFilter(String packagename){
		return basePackageFilter(Collections.singleton(packagename));
	}

	public static Predicate<String> basePackageFilter(String... packagenames){
		checkNotNull(packagenames,"packagenames is null");
		return basePackageFilter(FluentIterable.from(packagenames));
	}

	public static Predicate<String> basePackageFilter(Iterable<String> packagenames){
		return basePackageFilter(packagenames,false);
	}
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<String> basePackageFilter(Iterable<String> packagenames,boolean reverse){
		checkNotNull(packagenames,"packagenames is null");
		Set<String> _prefixes = CollectionUtils.toNonullLinkedHashSet(packagenames, CommonFunctions.normalizePackageNameFun());
		return Predicates.compose(
				new StartsWithPredicate(_prefixes,reverse), 
				CommonFunctions.normalizePackageNameFun());
	}

	public static Predicate<String> endsWithFilter(String suffix){
		return endsWithFilter(Collections.singleton(suffix));
	}

	public static Predicate<String> endsWithFilter(String... prefixes){
		checkNotNull(prefixes,"prefixes is null");
		return endsWithFilter(FluentIterable.from(prefixes));
	}
	
	public static Predicate<String> endsWithFilter(Iterable<String> suffixes){
		LinkedHashSet<String> _prefixes = FluentIterable.from(suffixes)
				.filter(Predicates.notNull())
				.copyInto(new LinkedHashSet<String>());
		return new EndsWithPredicate(_prefixes);
	}
	private static abstract class BaseWithStringPredicate implements Predicate<String>{
		private final LinkedHashSet<String> withs;
		private BaseWithStringPredicate(Iterable<String> withs) {
			this.withs = CollectionUtils.toNonullLinkedHashSet(checkNotNull(withs,"withs is null"), null);
		}
		protected abstract boolean doWith(String input,String with);
		@Override
		final public boolean apply(String input) {
			if(null != input) {
				for(String with : withs) {
					if(doWith(input,with)) {
						return true;
					}
				}
			}
			return false;
		}
	}
	private static class StartsWithPredicate extends BaseWithStringPredicate{
		private final boolean reverse;
		private StartsWithPredicate(Iterable<String> prefixes) {
			this(prefixes,false);
		}
		private StartsWithPredicate(Iterable<String> prefixes, boolean reverse) {
			super(prefixes);
			this.reverse = reverse;
		}
		@Override
		protected boolean doWith(String input,String with) {
			return reverse ? with.startsWith(input):input.startsWith(with);
		}
	}
	private static class EndsWithPredicate extends BaseWithStringPredicate{
		private EndsWithPredicate(Iterable<String> suffixes) {
			super(suffixes);
		}
		@Override
		protected boolean doWith(String input, String with) {
			return input.endsWith(with);
		}
	}
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Class<?>> publicClassFilter(){
		return new Predicate<Class<?>>() {
			
			@Override
			public boolean apply(Class<?> input) {
				if( null != input) {
					int modifier = input.getModifiers();
					if(Modifier.isPublic(modifier)) {
						try {
							if(input.isMemberClass()) {
								/** 要求成员类所有属外部类都是publi static */
								Class<?> enclosingClass = input.getEnclosingClass();
								do {
									if(!apply(enclosingClass)) {
										return false;
									}
									enclosingClass = enclosingClass.getEnclosingClass();
								}while(null != enclosingClass);
								return Modifier.isStatic(modifier);
							}
							
						} catch (Throwable e) {
							return false;
						}
						return true;
					}
				}
				return false;
			}
		};
	}
	private static class ClassModifierFilter  implements Predicate<Class<?>>{
		private final int modifier;
		public ClassModifierFilter(int modifier) {
			this.modifier = modifier;
		}
		@Override
		public boolean apply(Class<?> input) {
			if(null != input) {
				return (input.getModifiers() & modifier)==modifier;
			}
			return false;
		}
	}
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Class<?>> hasDefaultConstructorFilter() {
		return new Predicate<Class<?>>() {

			@Override
			public boolean apply(Class<?> input) {
				try {
					input.getConstructor();
					return true;
				} catch (Throwable e) {
					/** 
					 * 除了拦截方法声明的异常外，
					 * 还要拦截可能抛出的NoClassDefFoundError异常(比如Spring环境)，
					 * 所以这里要改为拦截Throwable.
					 */
					return false;
				}
			}};
	}
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Class<?>> noAbstractClassFilter() {
		return Predicates.not(new ClassModifierFilter(Modifier.ABSTRACT));
	}
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Member> constantFieldFilter(){
		return Predicates.and(fieldFilter(), 
				new MemberModifierFilter<>(Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL)) ;
	}
	
	@SuppressWarnings("unchecked")
	public static <M extends Member> Predicate<M> memberModifierFilter(int modifier,int...others){
		if(others != null) {
			for(int oth:others) {
				modifier |= oth;
			}
		}
		return (Predicate<M>) new MemberModifierFilter<>(modifier);
	}
	private static class MemberModifierFilter<M extends Member> implements Predicate<M>{
		private final int modifier;
		public MemberModifierFilter(int modifier) {
			this.modifier = modifier;
		}
		@Override
		public boolean apply(Member input) {
			if(null != input) {
				return (input.getModifiers() & modifier)==modifier;
			}
			return false;
		}
	}

	public static Predicate<Member> fieldFilter(){
		return new Predicate<Member>(){
			@Override
			public boolean apply(Member input) {
				return input instanceof Field;
			}
		};
	}

	public static Predicate<Member> methodFilter(){
		return new Predicate<Member>(){
			@Override
			public boolean apply(Member input) {
				return input instanceof Method;
			}
		};
	}

	public static Predicate<Class<?>> superTypFilter(Class<?>... classes){
		checkNotNull(classes,"classes is null");
		return superTypFilter(FluentIterable.from(classes));
	}
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Class<?>> superTypFilter(Iterable<Class<?>> classes){
		checkNotNull(classes,"classes is null");
		LinkedHashSet<Class<?>> _classes = CollectionUtils.toNonullLinkedHashSet(classes, null);
		return new SupperTypePredicate(_classes);
	}

	public static Predicate<Class<?>> subTypeClassFilter(Class<?>... classes){
		checkNotNull(classes,"classes is null");
		return subTypeClassFilter(FluentIterable.from(classes));
	}
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Class<?>> subTypeClassFilter(Iterable<Class<?>> classes){
		checkNotNull(classes,"classes is null");
		LinkedHashSet<Class<?>> _classes = CollectionUtils.toNonullLinkedHashSet(classes, null);
		return new SubTypePredicate(_classes);
	}

	public static Predicate<Class<?>> typeHierarchyFilter(boolean checkSubType, Class<?>... classes){
		return typeHierarchyFilter(checkSubType, FluentIterable.from(classes));
	}

	public static Predicate<Class<?>> typeHierarchyFilter(boolean checkSubType, Iterable<Class<?>> classes){
		checkNotNull(classes,"classes is null");
		return checkSubType 
				? subTypeClassFilter(classes)
				: superTypFilter(classes);
	}
	private static abstract class TypeHierarchyPredicate implements Predicate<Class<?>>{
		private final LinkedHashSet<Class<?>> withs;
		private TypeHierarchyPredicate(Iterable<Class<?>> withs) {
			this.withs = CollectionUtils.toNonullLinkedHashSet(checkNotNull(withs,"withs is null"), null);
		}
		protected abstract boolean doWith(Class<?> input,Class<?>with);
		@Override
		final public boolean apply(Class<?> input) {
			if(null != input) {
				for(Class<?> with : withs) {
					if(doWith(input,with)) {
						return true;
					}
				}
			}
			return false;
		}
	}
	private static class SubTypePredicate extends TypeHierarchyPredicate{

		public SubTypePredicate(Iterable<Class<?>> withs) {
			super(withs);
		}

		@Override
		protected boolean doWith(Class<?> input, Class<?> with) {
			return with.isAssignableFrom(input);
		}		
	} 
	private static class SupperTypePredicate extends TypeHierarchyPredicate{
		
		public SupperTypePredicate(Iterable<Class<?>> withs) {
			super(withs);
		}
		
		@Override
		protected boolean doWith(Class<?> input, Class<?> with) {
			return input.isAssignableFrom(with);
		}		
	} 

	public static Predicate<Member> publicStaticMethodFilter(){
		return Predicates.and(methodFilter(),memberModifierFilter(Modifier.PUBLIC,Modifier.STATIC));
	}

	public static Predicate<Member> supplierMethodFilter() {
		return new Predicate<Member>(){
			@Override
			public boolean apply(Member input) {
				if(input instanceof Method) {
					Method method = ((Method)input);
					return 0 == method.getParameterTypes().length && !void.class.equals(method.getReturnType());
				}
				return false;
			}
		};
	}

	public static Predicate<Member> publicStaticSupplierFilter() {
		return Predicates.or(publicStaticMethodFilter(),supplierMethodFilter());
	}
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Member> constSupplierMemberFilter(){
		return Predicates.or(constantFieldFilter(),publicStaticSupplierFilter());
	}
	private static class HasAnnotationFilter implements Predicate<Object> {
		private final LinkedHashSet<Class<? extends Annotation>> annotationTypes;
		private final boolean andOp;
		public HasAnnotationFilter(Iterable<Class<? extends Annotation>> annotationTypes,boolean andOp) {
			this.annotationTypes = CollectionUtils.toNonullLinkedHashSet(annotationTypes, null);
			this.andOp = andOp;
		}
		protected boolean doCheck(AnnotatedElement element, Class<? extends Annotation> annotationType) {
			return AnnotatedElementUtils.hasAnnotation(element,annotationType);
		}
		@Override
		public boolean apply(Object input) {
			if(input instanceof AnnotatedElement) {
				AnnotatedElement annotatedElement = (AnnotatedElement)input;
				if(andOp) {
					for(Class<? extends Annotation> type : annotationTypes) {
						if(!doCheck(annotatedElement,type)) {
							return false;
						}
					}
					return true;
				}else {
					for(Class<? extends Annotation> type : annotationTypes) {
						if(doCheck(annotatedElement,type)) {
							return true;
						}
					}
					return false;
				}
			}
			return false;
		}
	}
	private static class HasAnnotationDeclareLocally extends HasAnnotationFilter{

		public HasAnnotationDeclareLocally(Iterable<Class<? extends Annotation>> annotationTypes, boolean andOp) {
			super(annotationTypes, andOp);
		}

		@Override
		protected boolean doCheck(AnnotatedElement element, Class<? extends Annotation> annotationType) {
			return TypeMappedAnnotations.from(element).get(annotationType).isDirectlyPresent();
		}
		
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static <M extends AnnotatedElement>  Predicate<M> hasAnnotationFilter(Class<? extends Annotation> annotationType){
		return (Predicate) new HasAnnotationFilter(Collections.<Class<? extends Annotation>>singleton(annotationType),true);
	}

	public static Predicate<Class<?>> hasAnnotationClassFilter(Class<? extends Annotation> annotationType){
		return hasAnnotationFilter(annotationType);
	}
	@AoWeakCacheable(maximumSize = 200, expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Predicate<Member> hasAnnotationMemberFilter(Class<? extends Annotation> annotationType){
		return (Predicate)new HasAnnotationDeclareLocally(Collections.<Class<? extends Annotation>>singleton(annotationType),true);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static <M extends AnnotatedElement>  Predicate<M> hasAnnotationLocallyFilter(Class<? extends Annotation> annotationType){
		return (Predicate) new HasAnnotationDeclareLocally(Collections.<Class<? extends Annotation>>singleton(annotationType),true);
	}
	
	public static Predicate<Class<?>> hasAnnotationLocallyClassFilter(Class<? extends Annotation> annotationType){
		return hasAnnotationLocallyFilter(annotationType);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Predicate<Member> hasAnnotationLocallyMemberFilter(Class<? extends Annotation> annotationType){
		return (Predicate) new HasAnnotationDeclareLocally(Collections.<Class<? extends Annotation>>singleton(annotationType),true);
	}

	@SuppressWarnings({ "unchecked", "rawtypes" })
	@SafeVarargs
	public static <M extends AnnotatedElement>  Predicate<M> anyAnnotationFilter(Class<? extends Annotation>... annotationTypes){
		return (Predicate) new HasAnnotationFilter(CollectionUtils.toNonullLinkedHashSet(null, annotationTypes),false);
	}
	@SafeVarargs

	public static Predicate<Class<?>> anyAnnotationClassFilter(Class<? extends Annotation>... annotationTypes){
		return anyAnnotationFilter(annotationTypes);
	}
	@SafeVarargs
	public static Predicate<Method> anyAnnotationMethodFilter(Class<? extends Annotation>... annotationTypes){
		return anyAnnotationFilter(annotationTypes);
	}
	@SafeVarargs
	public static Predicate<Field> anyAnnotationFieldFilter(Class<? extends Annotation>... annotationTypes){
		return anyAnnotationFilter(annotationTypes);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@SafeVarargs
	public static <M extends AnnotatedElement>  Predicate<M> allAnnotatedFilter(Class<? extends Annotation>... annotationTypes){
		return (Predicate) new HasAnnotationFilter(CollectionUtils.toNonullLinkedHashSet(null, annotationTypes),true);
	}
	@SafeVarargs
	public static Predicate<Class<?>> allAnnotationClassFilter(Class<? extends Annotation>... annotationTypes){
		return allAnnotatedFilter(annotationTypes);
	}
	@SafeVarargs
	public static Predicate<Method> allAnnotationMethodFilter(Class<? extends Annotation>... annotationTypes){
		return allAnnotatedFilter(annotationTypes);
	}
	@SafeVarargs
	public static Predicate<Field> allAnnotationFieldFilter(Class<? extends Annotation>... annotationTypes){
		return allAnnotatedFilter(annotationTypes);
	}
	@SuppressWarnings("rawtypes")
	public static Predicate<Entry> notnullEntryFilter() {
		return new Predicate<Entry>() {

			@Override
			public boolean apply(Entry input) {
				return null != input && null!= input.getKey() && null!=input.getValue();
			}
		};
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static  Predicate<Map<String, Object>> containedEntryFilter(String key,Object value){
		return (Predicate)new ContainedEntry(Collections.singletonMap(checkNotNull(key,"key is null"), checkNotNull(value,"value is null")),true);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Predicate<Map<String, Object>> anyEntriesContainedFilter(Map<String, Object> entries){
		return (Predicate)new ContainedEntry(checkNotNull(entries,"entries is null") ,false);
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public static Predicate<Map<String, Object>> allEntriesContainedFilter(Map<String, Object> entries){
		return (Predicate)new ContainedEntry(checkNotNull(entries,"entries is null"),true);
	}
	@SuppressWarnings("unchecked")
	public static <E extends AnnotatedElement> Predicate<E> 
		containedEntryFilter(Class<? extends Annotation> annotationType,String key,Object value){
		return  (Predicate<E>) Predicates.compose(
				containedEntryFilter(key,value), 
				CommonFunctions.getAnnotationAttributesOfAnnotatedElementFun(annotationType));
	}
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static Predicate<Member> 
	containedEntryMemberFilter(Class<? extends Annotation> annotationType,String key,Object value){
		Function<Member, Map<String, Object>> fun = Functions.compose(
				CommonFunctions.getAnnotationAttributesOfAnnotatedElementFun(annotationType),
				CommonFunctions.asAnnotatedElementFun());
		return  Predicates.compose(
				containedEntryFilter(key,value), 
				fun);
	}
	@SuppressWarnings("unchecked")
	@AoWeakCacheable(expireAfterAccess = 20, expireAfterAccessTimeUnit = TimeUnit.SECONDS)
	public static <E extends AnnotatedElement> Predicate<E> 
	containedEntryAnnotatedElementFilter(Class<? extends Annotation> annotationType,String key,Object value){
		return  (Predicate<E>) Predicates.compose(
				containedEntryFilter(key,value), 
				CommonFunctions.getAnnotationAttributesOfAnnotatedElementFun(annotationType));
	}
	@SuppressWarnings("unchecked")
	public static <E extends AnnotatedElement> Predicate<E> 
		anyEntriesContainedFilter(Class<? extends Annotation> annotationType,Map<String, Object> entries){
		return  (Predicate<E>) Predicates.compose(
				anyEntriesContainedFilter(entries), 
				CommonFunctions.getAnnotationAttributesOfAnnotatedElementFun(annotationType));
	}
	@SuppressWarnings("unchecked")
	public static <E extends AnnotatedElement> Predicate<E> 
		allEntriesContainedFilter(Class<? extends Annotation> annotationType,Map<String, Object> entries){
		return  (Predicate<E>) Predicates.compose(
				allEntriesContainedFilter(entries), 
				CommonFunctions.getAnnotationAttributesOfAnnotatedElementFun(annotationType));
	}
	@SuppressWarnings("rawtypes")
	private static class ContainedEntry implements Predicate<Map>{
		private final Map<String, Object> required;
		private final boolean andOp;
		public ContainedEntry(Map<String, Object> required, boolean andOp) {
			this.required = ImmutableMap.copyOf( 
					Maps.<String,Object>filterEntries(
						checkNotNull(required,"required is null"), 
						notnullEntryFilter()));
			this.andOp = andOp;
		}
		@Override
		public boolean apply(Map input) {
			if(null != input) {
				if(andOp) {
					for(Entry<String, Object> entry : required.entrySet()) {
						Object value = input.get(entry.getKey());
						if(null == value || !value.equals(entry.getValue())) {
							return false;
						}
					}
					return true;
				}else {
					for(Entry<String, Object> entry : required.entrySet()) {
						Object value = input.get(entry.getKey());
						if(null != value && value.equals(entry.getValue())) {
							return true;
						}
					}
					return false;
				}
			}
			return false;
		}
	}
}
